#include "Task.hpp"
using namespace std;

const int processnum = 5;
vector<task_t> tasks;

//先描述
class channel
{
public:
    channel(int cmdfd,int slaverid,const string& processname)
        :_cmdfd(cmdfd)
        ,_slaverid(slaverid)
        ,_processname(processname)
    {}

    int _cmdfd;  //发送任务(管道)的文件描述符
    pid_t _slaverid; //子进程PID
    string _processname; //子进程名字：方便打印日志
};

void slaver()
{
    //read(0)
    while(true)
    {
        // cout<<"readfd: "<<rfd<<endl;
        int cmdcode = 0;
        //ssize_t read(int fd, void buf[.count], size_t count);
        int n = read(0,&cmdcode,sizeof(int));//读4字节 - cmdcode为读取到的内容
        if(n == sizeof(int))
        {
            //执行
            cout<<"slaver: "<<getpid()<<": cmdcode: "<<cmdcode<<endl;
            if(cmdcode>= 0 && cmdcode < tasks.size())
            {
                tasks[cmdcode]();
            }
        }
        if(n == 0)
        {
            break;
        }
        sleep(1);
    }
}

//输入型参数传参: const &
//输出： *
//输入输出： &
void InitProcessPool( vector<channel>* channels)
{
    //解决bug ： verson 2：确保每个子进程都只有一个写端
    vector<int> oldfds;
    for (size_t i = 0; i < processnum; i++)
    {
        int pipefd[2];//临时空间
        int n = pipe(pipefd);//输出型参数
        assert(!n);
        (void)n;

        pid_t id = fork();
        oldfds.push_back(pipefd[1]);
        //??????bug??????
        //子进程不断继承父进程上一次没关闭的写端口，但是当前子只将要关闭当前生成的写端，前面的都没管
        //然后打开的文件越来越多...
        if(id == 0)
        {
            cout<<getpid()<<" ";
            for(auto fd : oldfds)
            {
                 cout << "关闭写端："<<fd<<" ";
                close(fd);
            }
            cout<<endl;
            //child - 读端
            //close(pipefd[1]);
            //把标准输入更改-> 键盘改成管道
            dup2(pipefd[0],0);//****重定向****
            // slaver(pipefd[0]);//执行任务
            slaver();//改成0后就不用传参了
            cout<<"process: "<<getpid()<<" quit "<<endl;
            exit(0);
        }
        //parent - 写端
        close(pipefd[0]);//
        //添加字段
        string name = "process-"+ to_string(i);
        channels->push_back(channel(pipefd[1],id,name));
        sleep(1);
    }
    
}
void Debug(const vector<channel>& channels )
{
   for(auto& c : channels)
    {
        cout <<c._cmdfd<<" "<<c._slaverid<<" "<<c._processname<< endl;
    }
}
void Menu()
{
    cout<<"###############"<<endl;
    cout<<"# 0.exit      #"<<endl;
    cout<<"# 1.刷新日志   #"<<endl;
    cout<<"# 2.更新野区   #"<<endl;
    cout<<"# 3.检测更新   #"<<endl;
    cout<<"# 4.更新状态   #"<<endl;
    cout<<"###############"<<endl;

}
void ctrlSlaver(const vector<channel>& channels)
{
    int which = 0;
    //for (size_t i = 0; i < 100; i++)
    //int cnt = 10;
    while(true)//手动控制进程结束
    {
        int select = 0;
        Menu();
        cin>>select;
        if(select >= 5 || select <= 0) break;

        //1.选择任务
        // int cmdcode = rand()%(tasks.size());//写入管道的数据
        int cmdcode = select-1;//手动选择
        //2.选择进程
        // int processpos = rand()%channels.size();//随机选取子进程
        int processpos = (which++)%channels.size();
        //3.发送任务
        cout<<"parent: "<<cmdcode<<" already send to "<<channels[processpos]._cmdfd<<"-"<<channels[processpos]._processname << endl;
        write(channels[processpos]._cmdfd,&cmdcode,sizeof(cmdcode));
        sleep(1);
    }
            
}

void QuitProcess(const vector<channel>& channels)
{
    //解决问题 - 子进程继承父进程写端引发
    //version 1: 倒着回收进程

    // int last = channels.size()-1;
    // for (size_t i = last; i >=0 ; i--)
    // {
    //     close(channels[i]._cmdfd);
    //     waitpid(channels[i]._slaverid,nullptr,0);
    // }
    
    
    for(const auto &c:channels) 
    {
        close(c._cmdfd);//写端关闭 - 读端返回值会变成0
        waitpid(c._slaverid,nullptr,0);
        sleep(1);
    }

    // for(const auto&c:channels) 
    // {
    //     // waitpid(c._slaverid,nullptr,0);//等待子进程退出
    //     // sleep(2);
    // }
}
int main()
{
    //加载任务
    LoadTask(&tasks);

    srand(time(nullptr)^getpid()^1023);

    vector<channel> channels;
     //1.初始化
    InitProcessPool(&channels);
    //test
    // Debug(channels);

    //2.开始控制子进程
    ctrlSlaver(channels);
  
    //3.清理首尾 - 父进程结束(不写入了)
    QuitProcess(channels);

    return 0;
}